#pragma once

using namespace System;
using namespace System::Collections::Generic;

#include "Pattern.h"

namespace SpikingNeuronLib
{
	public ref class PatternGenerator abstract
	{
	protected:
		int _totalNeurons;
		Random^ _random;
		array<array<int>^>^ _neuronFiringData;

		void ClearNeuronDataArray()
		{
			for (int i = 0; i <	1000; i++)
			{
				_neuronFiringData[i] = nullptr;
			}
		}

	public:
		PatternGenerator(int totalNeurons) : _totalNeurons(totalNeurons)
		{
			_random = gcnew Random((int)DateTime::Now.ToFileTimeUtc());
		}

		// Abstract Methods -------------------------------------------------------------------------------------------

		virtual void Reset() abstract;						// reinitialise a random pattern
		virtual array<int>^ Select(int msec) abstract;		// select neuron indices that fire in the specified time slot (msec)
		virtual array<array<int>^>^ GetNeuronFiringData(int sec) abstract;	// get the neuron firing data for the specified second; for each msec timeslot, the array of neuron indices that fire in that timeslot
	};

	// Generate a background firing pattern where a single neuron is randomly selected for each time slot
	// Note that selection is from the total pool of all neurons
	// Note that all possible time slots are available (there is no pre-selection of firing times)
	public ref class SingleRandomBackgroundPatternGenerator : public PatternGenerator
	{
	public:
		SingleRandomBackgroundPatternGenerator(int totalNeurons);

		// Overridden Methods -------------------------------------------------------------------------------------------

		virtual void Reset() override;
		virtual array<int>^ Select(int msec) override;
		virtual array<array<int>^>^ GetNeuronFiringData(int sec) override { return _neuronFiringData; }
	};

	// Generate a background firing pattern where the firing times are at fixed times (selected time slots)
	// and the same firing pattern repeats each second
	// If an input pattern is provided, only neurons NOT in the input pattern will be fired in the background
	// Just one neuron is selected to fire in the background at each selected time slot
	public ref class FixedBackgroundPatternGenerator : public PatternGenerator
	{
	protected:
		HashSet<int>^ _inputPatternNeurons;						// a list of all input pattern neurons
		HashSet<int>^ _randomFiringTimes;						// a list of random firing times for the current second

		HashSet<int>^ CreateInputNeuronSet(Pattern^ inputPattern);

	public:
		FixedBackgroundPatternGenerator(int totalNeurons, List<int>^ firingTimes, Pattern^ inputPattern);

		void SetRandomFiringTimes(List<int>^ firingTimes);			// times in msec for firing a randomly selected neuron (max. 1000 msec)

		// Overridden Methods -------------------------------------------------------------------------------------------

		virtual void Reset() override;
		virtual array<int>^ Select(int msec) override;
		virtual array<array<int>^>^ GetNeuronFiringData(int sec) override { return _neuronFiringData; }
	};

	// Generate a background firing pattern where the firing times for each neuron are generated from a Poisson process
	// The background firing times of each neuron are set independently of other neurons
	// A raster frame is generated for each second, consisting of the independent firing times of each neuron for that second
	// The Reset() method generates a new raster frame and may be called at the beginning of each simulated second
	// The average firing frequency of each neuron is globally set for all neurons using the frequency parameter (defined in Hz)
	// Note that multiple neurons may be selected to fire in the background at each possible time slot
	public ref class PoissonProcessPatternGenerator : public PatternGenerator
	{
	protected:
		int _frequency;
		array<int>^ _offsets;
		HashSet<int>^ _excludeSet;						// a list of all neurons that are excluded from background firing

		inline int NextISI(int freq);
		void Initialise(int totalNeurons, int frequency, List<int>^ excludedNeurons);
		void CreateOneSecondRaster(array<int>^ offsets, int freq);

	public:
		PoissonProcessPatternGenerator(int totalNeurons, int frequency);
		PoissonProcessPatternGenerator(int totalNeurons, int frequency, List<int>^ excludedNeurons);

		// Overridden Methods -------------------------------------------------------------------------------------------

		virtual void Reset() override;
		virtual array<int>^ Select(int msec) override;
		virtual array<array<int>^>^ GetNeuronFiringData(int sec) override { return _neuronFiringData; }
	};
}
